package com.example.basecore.boot;

import com.example.basecore.command.CommandInterceptor;
import com.example.basecore.command.CommandInvocation;
import com.example.basecore.command.DefaultCommandBus;

import com.example.basecore.core.CmdExecutorAnnotation;
import com.example.basecore.core.InterceptorAnnotation;
import com.example.basecore.core.QueryExecutorAnnotation;
import com.example.basecore.dto.Command;
import com.example.basecore.executor.CommandExecutor;
import com.example.basecore.executor.QueryExecutor;
import com.example.basecore.factory.CommandExecutorFactory;
import com.example.basecore.factory.CommandInterceptorFactory;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.core.annotation.AnnotationAttributes;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;

import java.lang.annotation.Annotation;
import java.util.*;

/**
 * @author ycd0075
 * @date 2024-01-25 22:09
 */
public class CommandRegistry extends BaseRegistry implements FactoryBean<DefaultCommandBus>, ApplicationContextAware {
    private ApplicationContext context;

    @Override
    public DefaultCommandBus getObject(){
        return new DefaultCommandBus(new CommandInvocation(createCommandExecutorFactory(CmdExecutorAnnotation.class, QueryExecutorAnnotation.class),createCommandInterceptorFactory(InterceptorAnnotation.class)));
    }

    @Override
    public Class<?> getObjectType() {
        return DefaultCommandBus.class;
    }

    @Override
    public boolean isSingleton() {
        return true;
    }

    @Override
    public void setApplicationContext(ApplicationContext context) throws BeansException {
        this.context = context;
    }

    /**
     * @param commandExecutorType
     * @param queryExecutorType
     * @return
     */
    public CommandExecutorFactory createCommandExecutorFactory(Class<? extends Annotation> commandExecutorType, Class<? extends Annotation> queryExecutorType){

        // 处理CommandExecutor
        Map<String, Object> cmds = context.getBeansWithAnnotation(commandExecutorType);
        Map<Class<? extends Command>, CommandExecutor> executorMap = new HashMap(cmds.size());
        if(Objects.nonNull(cmds) && cmds.size() > 0){
            cmds.forEach((k,bean)->{
                Assert.isTrue(bean instanceof CommandExecutor,String.format("Bean [%s:%s] is not an instance of %s", k, bean.getClass(), CommandExecutor.class));
                CommandExecutor commandExecutor = (CommandExecutor)bean;
                AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(createStandardAnnotationMetadata(commandExecutor.getClass()).getAnnotationAttributes(commandExecutorType.getName()));
                Class<? extends Command> command = annotationAttributes.getClass(COMMAND);
                executorMap.put(command,commandExecutor);
            });
        }

        // 处理QueryExecutor
        Map<String, Object> querys = context.getBeansWithAnnotation(queryExecutorType);
        if(Objects.nonNull(querys) && querys.size() > 0){
            querys.forEach((k,bean)->{
                Assert.isTrue(bean instanceof QueryExecutor,String.format("Bean [%s:%s] is not an instance of %s", k, bean.getClass(), QueryExecutor.class));
                QueryExecutor queryExecutor = (QueryExecutor)bean;
                AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(createStandardAnnotationMetadata(queryExecutor.getClass()).getAnnotationAttributes(queryExecutorType.getName()));
                Class<? extends Command> command = annotationAttributes.getClass(COMMAND);
                executorMap.put(command,queryExecutor);
            });
        }
        return new CommandExecutorFactory(executorMap);
    }

    private CommandInterceptorFactory createCommandInterceptorFactory(Class<? extends Annotation> annotationType) {

        Map<String, Object> beans = context.getBeansWithAnnotation(annotationType);
        if(CollectionUtils.isEmpty(beans)){
            return null;
        }

        Map<Class<? extends Command>, Set<CommandInterceptor>> interceptorMap = new HashMap(beans.size());
        beans.forEach((k,bean)->{
            Assert.isTrue(bean instanceof CommandInterceptor,String.format("Bean [%s:%s] is not an instance of %s", k, bean.getClass(), CommandInterceptor.class));
            CommandInterceptor commandInterceptor = (CommandInterceptor)bean;
            AnnotationAttributes annotationAttributes = AnnotationAttributes.fromMap(createStandardAnnotationMetadata(commandInterceptor.getClass()).getAnnotationAttributes(annotationType.getName()));
            Class<? extends Command> command = annotationAttributes.getClass(COMMAND);
            if(CollectionUtils.isEmpty(interceptorMap.get(command))){
                Set<CommandInterceptor> interceptors = new HashSet();
                interceptors.add(commandInterceptor);
                interceptorMap.put(command,interceptors);
            }else{
                Set<CommandInterceptor> InterceptorSet = interceptorMap.get(command);
                InterceptorSet.add(commandInterceptor);
                interceptorMap.put(command,InterceptorSet);
            }
        });
        return new CommandInterceptorFactory(interceptorMap);

    }
}
